#include "Scene.h"

#include "PrimitiveSceneInfo.h"
#include "PrimitiveSceneProxy.h"
#include "Runtime/Logic/Component/PrimitiveComponent.h"
#include "Runtime/Thread/RenderingThreadContext.h"
#include "SceneUpdateCommandQueue.h"
#include "MeshPassProcessor.h"
#include "SceneRendering.h"

namespace Alice
{
    Scene::Scene(World* inWorld)
        :mWorld(inWorld)
    {
        mSceneUpdateCommandQueue=new SceneUpdateCommandQueue;
        mCachedPassMeshDrawList=new FCachedPassMeshDrawList[EMeshPass::Num];
    }

    void Scene::Update(RDGBuilder& GraphBuilder, const FUpdateParameters& Parameters)
    {
        std::vector<PrimitiveSceneInfo*> sceneInfosWithStaticDrawListUpdate;
        std::vector<PrimitiveSceneInfo*> addedPrimitiveSceneInfos,addedLocalPrimitiveSceneInfos;
        if(mSceneUpdateCommandQueue->mCommands.size()>0)
        {
            sceneInfosWithStaticDrawListUpdate.reserve(mSceneUpdateCommandQueue->mCommands.size());
            for(std::vector<FUpdateCommand*>::iterator iter=mSceneUpdateCommandQueue->mCommands.begin();iter!=mSceneUpdateCommandQueue->mCommands.end();iter++)
            {
                FUpdateCommand*updateCommand=*iter;
                if(updateCommand->mbAdded)
                {
                    addedPrimitiveSceneInfos.push_back((*iter)->mPrimitiveSceneInfo);
                }
                sceneInfosWithStaticDrawListUpdate.push_back((*iter)->mPrimitiveSceneInfo);
            }
        }
        for(auto iter=addedPrimitiveSceneInfos.begin();iter!=addedPrimitiveSceneInfos.end();iter++)
        {
            addedLocalPrimitiveSceneInfos.push_back(*iter);
        }
        FTask updateUniformExpressionTask;
        FTask addStaticMeshesTask;
        while (addedLocalPrimitiveSceneInfos.size()>0)
        {
            PrimitiveSceneInfo*primitiveSceneInfo=addedPrimitiveSceneInfos[addedLocalPrimitiveSceneInfos.size()-1];
            mPrimitives.push_back(primitiveSceneInfo);
            addedLocalPrimitiveSceneInfos.pop_back();
        }
        if(mSceneUpdateCommandQueue->mCommands.size()>0)
        {
            PrimitiveSceneInfo::AddStaticMeshes(*GraphBuilder.mCGICommandList,this,sceneInfosWithStaticDrawListUpdate,false);
        }
        if(Parameters.mPostStaticMeshUpdate!=nullptr)
        {
            //Parameters.mPostStaticMeshUpdate();
        }
        addStaticMeshesTask.Wait();
        PrimitiveSceneInfo::CacheMeshDrawCommands(this,sceneInfosWithStaticDrawListUpdate);
    }
    void Scene::AddPrimitive(PrimitiveComponent* inPrimitiveComponent)
    {
        PrimitiveSceneProxy*primitiveSceneProxy=inPrimitiveComponent->CreateSceneProxy();
        inPrimitiveComponent->mPrimitiveSceneProxy=primitiveSceneProxy;
        PrimitiveSceneInfo*primitiveSceneInfo=new PrimitiveSceneInfo(inPrimitiveComponent,this);
        primitiveSceneProxy->mPrimitiveSceneInfo=primitiveSceneInfo;
        ENQUEUE_RENDER_COMMAND(AddPrimitiveCommand)([this,addedPrimitiveInfo=std::move(primitiveSceneInfo)](CGICommandListImmediate& inCGICommandList)
        {
            AddPrimitiveSceneInfo_RenderThread(addedPrimitiveInfo);
        });
    }
    void Scene::AddPrimitiveSceneInfo_RenderThread(PrimitiveSceneInfo* inPrimitiveSceneInfo)
    {
        mSceneUpdateCommandQueue->EnqueueAdd(inPrimitiveSceneInfo);
    }
}
